package com.augurit.smartform.database;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.augurit.common.entity.SysMetaTable;
import com.augurit.common.util.lang.StringUtils;
import com.augurit.smartform.constant.ColumnTypeConstant;
import com.augurit.smartform.constant.PageConstant;
import com.augurit.smartform.constant.SqlTypeConstant;
import com.augurit.smartform.constant.TypeConstant;
import com.augurit.smartform.database.dialect.Dialect;
import com.augurit.smartform.factory.DialectFactory;
import com.augurit.smartform.service.ISmartFormService;
import com.augurit.smartform.util.XMLHelper;

/**
 * 将XML字符串转换为JAVA对象
 * @author cuigeqiang
 *
 */
@Component
public class DatabaseParser {
	@Autowired
	private DialectFactory dialectFactory;
	@Autowired
	private ISmartFormService smartFormService;
	
	/**
	 * 解析XML字符串并顺序添加子表到LIST中入口
	 * @param tables
	 * @return
	 * cuigeqiang
	 */
	public List<Table> parseToAscTables(String sourceXmlStr){
		List<Table> tables=this.parse(sourceXmlStr);
		List<Table> deepTables=this.getAscDeepTables(tables);
		return deepTables;
	}
	
	/**
	 * 解析XML字符串并逆序添加子表到LIST中入口
	 * @param tables
	 * @return
	 * cuigeqiang
	 */
	public List<Table> parseToDescTables(String sourceXmlStr){
		List<Table> tables=this.parse(sourceXmlStr);
		List<Table> deepTables=this.getDescDeepTables(tables);
		return deepTables;
	}
	
	/**
	 * 将XML字符串信息提取并存放到TABLE对象中
	 * @param sourceXmlStr
	 * @return
	 * cuigeqiang
	 */
	public List<Table> parse(String sourceXmlStr){
		List<Table> tables=new ArrayList<Table>();
		Document document=XMLHelper.getDocument(sourceXmlStr);
//		List<Element> objects=(List<Element>)document.selectNodes("//objects/object");
//		Iterator it=objects.iterator();
//		while(it.hasNext()){
//			Element element=(Element)it.next();
//			Table table=this.parseElementToTable(element);
//			tables.add(table);
//		}
//		Element element=document.getRootElement();
//		Table table=this.parseElementToTable(element);
//		tables.add(table);		
		Iterator it=document.nodeIterator();
		while(it.hasNext()){
			Element element=(Element)it.next();
			Table table=this.parseElementToTable(element,sourceXmlStr);
			tables.add(table);
		}
		return tables;
	}
	
	/**
	 * 顺序添加子表到LIST中入口
	 * @param tables
	 * @return
	 * cuigeqiang
	 */
	public List<Table> getAscDeepTables(List<Table> tables){
		List<Table> deepTables=new ArrayList<Table>();
		for(int i=0;i<tables.size();i++){
			Table table=tables.get(i);
			deepTables.add(table);
			this.getAscDeepTablesFromChildren(deepTables, table);
		}
		return deepTables;
	}
	
	/**
	 * 顺序添加子表到LIST中
	 * @param deepTables
	 * @param table
	 * cuigeqiang
	 */
	private void getAscDeepTablesFromChildren(List<Table> deepTables,Table table){
		List<Table> childTables=table.getChildTables();
		for(int i=0;i<childTables.size();i++){
			Table childTable=childTables.get(i);
			deepTables.add(childTable);
			this.getAscDeepTablesFromChildren(deepTables, childTable);
		}
	}
	
	/**
	 * 逆序添加子表到LIST中入口
	 * @param tables
	 * @return
	 * cuigeqiang
	 */
	public List<Table> getDescDeepTables(List<Table> tables){
		List<Table> deepTables=new ArrayList<Table>();
		for(int i=0;i<tables.size();i++){
			Table table=tables.get(i);
			this.getDescDeepTablesFromChildren(deepTables, table);
			deepTables.add(table);
		}
		return deepTables;
	}
	
	/**
	 * 逆序添加子表到LIST中
	 * @param deepTables
	 * @param table
	 * cuigeqiang
	 */
	private void getDescDeepTablesFromChildren(List<Table> deepTables,Table table){
		List<Table> childTables=table.getChildTables();
		for(int i=0;i<childTables.size();i++){
			Table childTable=childTables.get(i);
			this.getDescDeepTablesFromChildren(deepTables, childTable);
			deepTables.add(childTable);
		}
	}
		
	/**
	 * 将DOM4J的DOCUMENT转换为TABLE的入口
	 * @param element
	 * @return
	 * cuigeqiang
	 */
	private Table parseElementToTable(Element element,String sourceXmlStr){
		Table table=new Table();
		Table parentTable=null;
		table.setSmartform(true);
		table.setSourceXmlStr(sourceXmlStr);
		this.parseElementToTable(table, element);
		parentTable=this.addExternalParentTable(element, table);
		return parentTable;
	}
	
	/**
	 * 将DOM4J的DOCUMENT转换为TABLE类
	 * @param table
	 * @param element
	 * cuigeqiang
	 */
	private void parseElementToTable(Table table,Element element){
		Iterator attrs=element.attributeIterator();
		this.parseAttrIteratorToTableAttr(table, attrs);
//		this.adjustTableVersion(table);								//调整表单的版本号
		this.setFullName(table);									//设置表的全名
		
		this.addIdColumn(table);
//		List<Element> fields=element.selectNodes("//field");
		List<Element> fields=element.elements("field");
		this.parseElementsToFields(table, fields);
		
		List<Element> childElements=element.elements("object");
		this.parseElementsToTables(table, childElements);
	}
	
	/**
	 * 将DOM4J属性转换为TABLE属性
	 * @param table
	 * @param attrs
	 * cuigeqiang
	 */
	private void parseAttrIteratorToTableAttr(Table table,Iterator attrs){
		while(attrs.hasNext()){
			Attribute attr=(Attribute)attrs.next();
			if("id".equals(attr.getName())){
				table.setPageId(attr.getValue());
			}else if("name".equals(attr.getName())){
				table.setName(attr.getValue());
			}else if("cnName".equals(attr.getName())){
				table.setCnName(attr.getValue());
			}else if("category".equals(attr.getName())){
				table.setCategory(attr.getValue());
			}else if("desciption".equals(attr.getName())){
				table.setDescription(attr.getValue());
			}else if("version".equals(attr.getName())){
				table.setVersion(Integer.parseInt(attr.getValue()));
			}
		}
		//调整子表单所属的目录
		if(table.getParent()!=null&&StringUtils.isNotBlank(table.getParent().getCategory())
				&&StringUtils.isBlank(table.getCategory())){
			table.setCategory(table.getParent().getCategory());
		}
	}
	
	/**
	 * 调整数据库表的版本号
	 * @param table
	 * cuigeqiang
	 */
	private void adjustTableVersion(Table table){
		SysMetaTable lastedSmForm=smartFormService.getLastedSmFormByName(table.getName());
		if(lastedSmForm==null){
//			table.setVersion(1);
		}else if(lastedSmForm.getPageId().equals(table.getPageId())&&lastedSmForm.getVersion().intValue()<table.getVersion()){
//			table.setVersion(lastedSmForm.getVersion().intValue()+1);
		}else if(lastedSmForm.getPageId().equals(table.getPageId())&&lastedSmForm.getVersion().intValue()>=table.getVersion()){
//			table.setVersion(lastedSmForm.getVersion());
		}else{
			throw new RuntimeException("系统已经存在名称为"+table.getName()+"的表了！");
		}
	}
	
	/**
	 * 设置表的全名
	 * @param table
	 * cuigeqiang
	 */
	private void setFullName(Table table){
		table.setFullName(table.getName()+PageConstant.VERSION_FLAG+table.getVersion());
	}
	
	/**
	 * 将DOM4J的ELEMENT元素转换成FIELD对象
	 * @param table
	 * @param elements
	 * cuigeqiang
	 */
	private void parseElementsToFields(Table table,List<Element> elements){
		for(int i=0;i<elements.size();i++){
			Element element=elements.get(i);
			Iterator attrs=element.attributeIterator();
			Column column=new Column();
			this.parseAttrIteratorToColumnAttr(table, column, attrs);
		}
	}
	
	/**
	 * 将DOM4J属性转换为FILED对象的属性
	 * @param table
	 * @param column
	 * @param attrs
	 * cuigeqiang
	 */
	private void parseAttrIteratorToColumnAttr(Table table,Column column,Iterator attrs){
		while(attrs.hasNext()){
			Attribute attr=(Attribute)attrs.next();
			if("id".equals(attr.getName())){
				column.setFieldId(attr.getValue());
			}else if("name".equals(attr.getName())){
				column.setName(attr.getValue());
			}else if("cnName".equals(attr.getName())){
				column.setCnName(attr.getValue());
			}else if("isUnique".equals(attr.getName())){
				if("true".equalsIgnoreCase(attr.getValue())){
					column.setUnique(true);
				}
			}else if("isNotNull".equals(attr.getName())){
				if("true".equalsIgnoreCase(attr.getValue())){
					column.setNotNull(true);
				}
			}else if("isIndex".equals(attr.getName())){
				if("true".equalsIgnoreCase(attr.getValue())){
					column.setIndex(true);
				}
			}else if("javaType".equals(attr.getName())){
				column.setSqlType(attr.getValue());
			}else if("tag".equals(attr.getName())){									//去掉RIDIO的相同名称字段
				column.setTag(attr.getValue());
			}else if("maxlength".equals(attr.getName())){
				if(StringUtils.isNotBlank(attr.getValue())){
					column.setLength(Integer.parseInt(attr.getValue()));
				}
			}else if("defaultValue".equals(attr.getName())){
				column.setDefaultValue(attr.getValue());
			}else if("dictionary".equals(attr.getName())){
				if(StringUtils.isNotBlank(attr.getValue())){
					column.setDictionary(attr.getValue());
				}
			}else if("precision".equals(attr.getName())){
				if(StringUtils.isNotBlank(attr.getValue())){
					column.setPrecision(Integer.parseInt(attr.getValue()));
				}
			}else if("scale".equals(attr.getName())){
				if(StringUtils.isNotBlank(attr.getValue())){
					column.setScale(Integer.parseInt(attr.getValue()));
				}
			}else if("checked".equals(attr.getName())){
				if("true".equalsIgnoreCase(attr.getValue())){
					column.setChecked(true);
				}
			}else if("value".equals(attr.getName())){
				column.setCheckedValue(attr.getValue());
			}else if("isSummary".equals(attr.getName())){
				if("true".equalsIgnoreCase(attr.getValue())){
					column.setSummary(true);
				}
			}
		}
		
		if(this.checkColumns(table, column)){										//检查字段是否应该添加到TABLE中
			column.setObjectName(table.getName());									//加入字段所属表名（不含版本信息）
			column.setTable(table.getFullName());									//加入字段所属表名
			this.setDefaultValueWithChecked(column);								//加入单选框等默认值
			this.registerConstraintToTable(table, column);
			this.setDbType(column);
			this.setSQLDefaultValue(column);
			
			this.registerSummaryToTable(table, column);								//将汇总字段注册到TABLE对象中（工作流使用）
			this.setColumnType(column);												//将SQLTYPE转换成为COLUMNTYPE（工作流使用）
			this.setPropertyName(column);											//设置字段的JAVA属性名称（工作流使用）
			
			table.setColumn(column);
		}
	}
	
	/**
	 * 将约束注册到TABLE对象中
	 * @param table
	 * @param column
	 * cuigeqiang
	 */
	private void registerConstraintToTable(Table table,Column column){
		if(column.isIndex()){
			table.setIndexColumns(column.getName());
		}
		if(column.isUnique()){
			table.setUniqueColumns(column.getName());
		}
	}
	
	/**
	 * 将汇总字段注册到TABLE对象中（工作流使用）
	 * @param table
	 * @param column
	 * cuigeqiang
	 */
	private void registerSummaryToTable(Table table,Column column){
		if(column.isSummary()){
			table.setSummaryColumns(column.getName());
		}
	}
	
	/**
	 * 将JAVA类型转换为数据库类型存放到COLUMN对象中
	 * @param column
	 * cuigeqiang
	 */
	private void setDbType(Column column){
		Dialect dialect=dialectFactory.getDialect();
		int length=0;
		int precision=0;
		int scale=0;
		if(column.getLength()!=null){
			length=column.getLength();
		}
		if(column.getPrecision()!=null){
			precision=column.getPrecision();
		}
		if(column.getScale()!=null){
			scale=column.getScale();
		}
		String dbType=dialect.getTypeName(column.getSqlType(), length, precision, scale);
		column.setDbType(dbType);
	}
	
	/**
	 * 将SQLTYPE转换成为COLUMNTYPE（工作流使用）
	 * @param column
	 * cuigeqiang
	 */
	private void setColumnType(Column column){
		if(SqlTypeConstant.BIT.equals(column.getSqlType())
			||SqlTypeConstant.BIGINT.equals(column.getSqlType())
			||SqlTypeConstant.SMALLINT.equals(column.getSqlType())
			||SqlTypeConstant.TINYINT.equals(column.getSqlType())
			||SqlTypeConstant.INTEGER.equals(column.getSqlType())
			||SqlTypeConstant.NUMERIC.equals(column.getSqlType())
			||SqlTypeConstant.FLOAT.equals(column.getSqlType())
			||SqlTypeConstant.DOUBLE.equals(column.getSqlType())){
			column.setColumnType(ColumnTypeConstant.NUMBER);
		}else if(SqlTypeConstant.CHAR.equals(column.getSqlType())
			||SqlTypeConstant.VARCHAR.equals(column.getSqlType())){
			column.setColumnType(ColumnTypeConstant.VARCHAR2);
		}else if(SqlTypeConstant.DATE.equals(column.getSqlType())
			||SqlTypeConstant.TIME.equals(column.getSqlType())
			||SqlTypeConstant.TIMESTAMP.equals(column.getSqlType())){
			column.setColumnType(ColumnTypeConstant.DATE);
		}else{
			column.setColumnType(column.getSqlType());
		}
	}
	
	/**
	 * 设置字段的JAVA属性名称（工作流使用）
	 * @param column
	 * cuigeqiang
	 */
	private void setPropertyName(Column column){
		column.setPropertyName(StringUtils.toJavaName(column.getName()));
	}
	
	/**
	 * 根据JAVA类型的不同转换成不同的默认值
	 * @param column
	 * cuigeqiang
	 */
	private void setSQLDefaultValue(Column column){
		Dialect dialect=dialectFactory.getDialect();
		if(StringUtils.isNotBlank(column.getDefaultValue())){
			String sqlDefaultValue=dialect.getDefaultValue(column.getSqlType(), column.getDefaultValue());
			column.setSqlDefaultValue(sqlDefaultValue);
		}
	}
	
	/**
	 * 根据JAVA类型的不同转换成不同的值
	 * @param column
	 * cuigeqiang
	 */
	public void setSQLValue(Column column){
		Dialect dialect=dialectFactory.getDialect();
		if(StringUtils.isNotBlank(column.getValue())){
			String sqlValue=dialect.getDefaultValue(column.getSqlType(), column.getValue());
			column.setSqlValue(sqlValue);
		}
	}
	
	/**
	 * 根据JAVA类型的不同转换成不同的值
	 * @param column
	 * cuigeqiang
	 */
	public void setQuerySQLValue(Column column){
		Dialect dialect=dialectFactory.getDialect();
		if(StringUtils.isNotBlank(column.getValue())){
			String sqlQueryValue=dialect.getQueryValue(column.getSqlType(), column.getValue());
			column.setSqlQueryValue(sqlQueryValue);
		}
	}
	
	/**
	 * 为TABLE增加主键列
	 * @param table
	 * cuigeqiang
	 */
	private void addIdColumn(Table table){
		Column idColumn=new Column();
		idColumn.setName("ID");
		idColumn.setCnName("主键");
		idColumn.setSqlType(SqlTypeConstant.BIGINT);
		idColumn.setPrimaryKey(true);
		idColumn.setTable(table.getFullName());
		idColumn.setObjectName(table.getName());									//加入字段所属表名（不含版本信息）
		this.setDbType(idColumn);
		
		this.setColumnType(idColumn);												//将SQLTYPE转换成为COLUMNTYPE（工作流使用）
		this.setPropertyName(idColumn);												//设置字段的JAVA属性名称（工作流使用）
		
		table.setPkColumn(idColumn.getName());
		table.setColumn(idColumn);
	}
	
	/**
	 * 将DOM4J的ELEMENTS转换为TABLES
	 * @param parent
	 * @param childElements
	 * cuigeqiang
	 */
	private void parseElementsToTables(Table table,List<Element> childElements){
		for(int i=0;i<childElements.size();i++){
			Element childElement=childElements.get(i);
			Table childTable=new Table();
			childTable.setSourceXmlStr(table.getSourceXmlStr());
			childTable.setSmartform(true);
			childTable.setParent(table);
			this.parseElementToTable(childTable,childElement);
			this.addFkColumn(table, childTable);
			table.setChildTables(childTable);
		}
	}
	
	/**
	 * 为子表添加外键列
	 * @param parent
	 * @param table
	 * cuigeqiang
	 */
	private void addFkColumn(Table parent,Table table){
		Column fkColumn=new Column();
		fkColumn.setName(parent.getFullName()+"_ID");
		fkColumn.setCnName("外键");
		fkColumn.setSqlType(SqlTypeConstant.BIGINT);
		fkColumn.setForeignKey(true);
		fkColumn.setTable(table.getFullName());
		fkColumn.setRefColumn(parent.getPkColumn());
		fkColumn.setRefTable(parent.getFullName());
		fkColumn.setObjectName(table.getName());									//加入字段所属表名（不含版本信息）
		this.setDbType(fkColumn);
		
		this.setColumnType(fkColumn);												//将SQLTYPE转换成为COLUMNTYPE（工作流使用）
		this.setPropertyName(fkColumn);												//设置字段的JAVA属性名称（工作流使用）
		
		FKRelation relation=new FKRelation();
		relation.setFkColumn(fkColumn.getName());
		relation.setRefTable(parent.getFullName());
		relation.setRefColumn(parent.getPkColumn());
		table.setFKRelation(relation);
		
		table.setColumn(fkColumn);
	}
	
	/**
	 * 检查字段是否应该添加到TABLE中
	 * @param table
	 * @param column
	 * @return
	 * cuigeqiang
	 */
	private boolean checkColumns(Table table,Column column){
		boolean passed=true;
		if(StringUtils.isBlank(column.getSqlType())){										//去掉没有设置JAVATYPE的字段
			passed=false;
		}else if(TypeConstant.RADIO.equalsIgnoreCase(column.getTag())){							//去掉有相同名称的RIDIO字段
			List<Column> columns=table.getColumn();
			for(Column col : columns){
				if(col.getName().equalsIgnoreCase(column.getName())){
					passed=false;
					break;
				}
			}
		}
		return passed;
	}
	
	/**
	 * 加入默认值
	 * @param column
	 * cuigeqiang
	 */
	private void setDefaultValueWithChecked(Column column){
		if(column.isChecked()){
			column.setDefaultValue(column.getCheckedValue());
		}
	}
	
	/**
	 * 从其他页面添加父表单
	 * @param element
	 * @param table
	 * cuigeqiang
	 */
	private Table addExternalParentTable(Element element,Table table){
		Table topTable=table;
		String parentName=element.attributeValue("parentName");
		if(StringUtils.isNotBlank(parentName)){
			SysMetaTable sysMetaTable=smartFormService.getLastedSmFormByName(parentName);
			if(sysMetaTable!=null){
				String xml=sysMetaTable.getRawXml();
				Document document=XMLHelper.getDocument(xml);
				Element parentElement=(Element)document.node(0);
				Table parentTable=this.parseElementToTable(parentElement,xml);
				table.setParent(parentTable);
				this.addFkColumn(parentTable, table);
				parentTable.setChildTables(table);
				topTable=parentTable;
			}
		}
		return topTable;
	}
}
